#include "ffmpegutils.h"
#include <QFile>
#include <QDir>


#define IN_DATA_SIZE 20480
#define THRESHOLD 2048


FFMPEGUtils::FFMPEGUtils()
{

}

int decode(AVCodecContext* codecContext,AVPacket* packet,AVFrame* frame,QFile &outFile) {


    int res = avcodec_send_packet(codecContext,packet);

    if(res != 0) {

        MSGERR(res);
        qDebug() << errBuf;
        return res;
    }

    while (true) {

        /**
         * Return decoded output data from a decoder.
         *
         * @param avctx codec context
         * @param frame This will be set to a reference-counted video or audio
         *              frame (depending on the decoder type) allocated by the
         *              decoder. Note that the function will always call
         *              av_frame_unref(frame) before doing anything else.
         *
         * @return
         *      0:                 success, a frame was returned
         *      AVERROR(EAGAIN):   output is not available in this state - user must try
         *                         to send new input
         *      AVERROR_EOF:       the decoder has been fully flushed, and there will be
         *                         no more output frames
         *      AVERROR(EINVAL):   codec not opened, or it is an encoder
         *      AVERROR_INPUT_CHANGED:   current decoded frame has changed parameters
         *                               with respect to first decoded frame. Applicable
         *                               when flag AV_CODEC_FLAG_DROPCHANGED is set.
         *      other negative values: legitimate decoding errors
         */
        res = avcodec_receive_frame(codecContext,frame);

        if(res == AVERROR(EAGAIN) || res == AVERROR_EOF){

            return 0;
        }else if(res < 0) {

            MSGERR(res);
            qDebug() << errBuf;
            return res;
        }

        outFile.write((char*)frame->data[0],frame->linesize[0]);
    }

}

void FFMPEGUtils::pcmAACDecode(PCMSpec &pcmSpec)
{


    QFile inFile(pcmSpec.inPath);
    QFile outFile(pcmSpec.outPath);

    AVCodec* codec = nullptr;
    AVCodecParserContext* codecParserContext = nullptr;
    AVCodecContext* codecContext = nullptr;
    int res;
    AVPacket* packet = nullptr;
    AVFrame* frame = nullptr;
    char* aacBuff = nullptr;
    char* mvAACPoint = nullptr;
    int len = 0;
    int readFinish = 0;


    codec = avcodec_find_decoder_by_name("libfdk_aac");

    if(codec == nullptr) {

        qDebug() << "not find libfdk_aac";
        return;
    }

    codecParserContext = av_parser_init(codec->id);

    if(codecParserContext == nullptr) {

        qDebug() << "av_parser_init fail";
        return;
    }

    codecContext = avcodec_alloc_context3(codec);

    if (codecContext == nullptr) {

        qDebug() << "avcodec_alloc_context3 fail";
        return;
    }

    res = avcodec_open2(codecContext,codec,nullptr);

    if(res != 0) {

        MSGERR(res);
        qDebug() << errBuf;
        goto end;
    }


    packet = av_packet_alloc();

    if(packet == nullptr) {

        qDebug() << "av_packet_alloc fail";
        goto end;
    }

    frame = av_frame_alloc();

    if(frame == nullptr) {

        qDebug() << "av_frame_alloc fail";
        goto end;
    }


    if(!inFile.open(QFile::ReadOnly)) {


        qDebug() << "open " << pcmSpec.inPath << " fail";
        goto end;
    }

    if(!outFile.open(QFile::WriteOnly)) {

        qDebug() << "open " << pcmSpec.outPath << " fail";
        goto end;
    }

    aacBuff = new char[IN_DATA_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    mvAACPoint = aacBuff;

    len = inFile.read(mvAACPoint,IN_DATA_SIZE);

    while (len > 0) {


        int parserLen = av_parser_parse2(codecParserContext,codecContext,
                                         &packet->data,&packet->size,
                                         (uint8_t*)mvAACPoint,len,
                                         AV_NOPTS_VALUE,AV_NOPTS_VALUE,0);

        mvAACPoint+= parserLen;
        len -= parserLen;

       if(packet->size > 0 && decode(codecContext,packet,frame,outFile) != 0) {

           goto end;
       }

       if(len < THRESHOLD && readFinish == 0) {

           memmove(aacBuff,mvAACPoint,len);
           mvAACPoint = aacBuff;

           int readLen = inFile.read(mvAACPoint+len,IN_DATA_SIZE - len);

           if(readLen == 0) {

               readFinish = 1;
           }else{

               len += readLen;
           }
       }

    }
    // 刷新缓存
    decode(codecContext,nullptr,frame,outFile);

    pcmSpec.channelLayout = codecContext->channel_layout;
    pcmSpec.sampleRate = codecContext->sample_rate;
    pcmSpec.sampleFormat = codecContext->sample_fmt;


end:

    if(aacBuff != nullptr) {

        delete [] aacBuff;
    }

    outFile.close();
    inFile.close();

    av_frame_free(&frame);
    av_packet_free(&packet);

    if(codecContext != nullptr) {

        avcodec_free_context(&codecContext);
    }

}

